<script>

  // 模拟视图更新的
  function updateView() {
    console.log("更新视图~");
  }


  // ["push","pop","shift","unshift","splice"] 位于Array.prototype
  const oldArrayProerty = Array.prototype;
  const arrProto = Object.create(oldArrayProerty);
  ["push", "pop", "shift", "unshift", "splice"].forEach(methodName => {
    // 重写了7个方法
    arrProto[methodName] = function () {
      // 为什么是重写这7个方法
      // 答： 需要要加入更新视图的逻辑
      updateView();

      // 调用原生的push ,pop, shift... 
      oldArrayProerty[methodName].call(this, ...arguments)
    }
  })


  function defineReactive(target, key, value) {
    // console.log("target:", target);
    // console.log("key:", key);
    // console.log("value:", value);

    // 如果value是一个对象，需要深度侦听
    observer(value)

    // 给target这个对象上，重新定义name,age,info
    Object.defineProperty(target, key, {
      get() {
        console.log("get...");
        return value;
      },
      set(newValue) {
        if (newValue !== value) {
          // 如果newValue也是一个对象，也需要深度侦听
          observer(newValue)

          console.log("set...");
          value = newValue;
          // 更新视图
          updateView();
        }
      }
    })
  }

  function observer(target) {

    if (typeof target !== "object" || target === null) {
      // target 不是对象或数组
      return target;
    }

    if (Array.isArray(target)) {
      // arrProto 是一个对象，这个对象上重新了那7个方法
      // 重写逻辑中添加了更新视图的逻辑
      target.__proto__ = arrProto
    }

    for (let key in target) {
      // 需要把name，age，info处理成响应式
      // 也就是对数据进行侦听和劫持
      // console.log("key:", key);
      defineReactive(target, key, target[key]);
    }
  }

  // 对数据进行侦听
  // 所谓的侦听就是获取数据时，需要知道
  // 去改变数据时，也需要知道，最终需要更新视图
  let data = {
    name: "ml",
    age: 18,
    info: {
      address: "bj",
    },
    nums: [10, 20, 30]
  }
  observer(data);

  // 测试
  // console.log(data.nums)
  
  // 这个push不是原生的push,是yyx自己重写后的push
  // 一共有7个方法：push pop shift unshift splice sort reverse
  data.nums.push(40)

  // Object.defineProperty有什么缺点？
  // 优点：兼容性好
  // 缺点：
  //   1）需要深度递归监听，只是深度递归，肯定有性能问题。
  //   2）无法监听新增属性和删除属性 ，解决：Vue.set  Vue.delete
  //   3）无法监听数组相关的方法，需要特殊处理。
  //   4）在vue3中，响应式就没有使用Object.defineProperty，使用了proxy。
</script>